package com.hrt.datasender.datacollect.serial;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.List;

import com.btcode.common.MyLog;
import com.btcode.exception.MsgException;
import com.btcode.log.ILog;
import com.hrt.datasender.datacollect.serial.protocol.ISerialDataProtocol;
import com.hrt.datasender.datadao.model.BcSerialModel;
import com.hrt.datasender.mq.MqKit;
import com.hrt.datasender.mq.MqKit.MsgListener;
import com.hrt.datasender.mq.model.MqModel;
import com.hrt.datasender.mq.model.SerialModel;

import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;

/**
 * 串口数据传输， 负责消费消息队列中的询问数据消息，往串口发送询问数据的报文， 同时获取从串口中返回的数据，发布到消息队列中，供设备驱动读取
 * 
 * @author gislin
 *
 */
public class SerialDataTransport implements Runnable {
    
    private final int comWaitTime = 2000;
    
    private final int askInterval = 967;
    
    private final int millTrans = 1000;
    
    private final int readBufferLength = 200;

    private ILog log;
    private OutputStream out;
    private InputStream in;
    private BcSerialModel bcSerialModel;
    /**
     * 串口数据协议解析器，负责基础的粘包拆包，数据身份识别的工作，以便后续数据分发
     */
    private ISerialDataProtocol dataProtocol;
    private Date lastAskDate = new Date();

    public SerialDataTransport(BcSerialModel bcSerialModel) {

        this.bcSerialModel = bcSerialModel;
        log = MyLog.getInstance().getLogger(getClass().getName() + "." + bcSerialModel.getComNum());

        try {
            this.dataProtocol = getDataProtocol(bcSerialModel.getDataProtocol());
        }
        catch (Exception e) {
            log.error("获取串口数据协议出错", e);
            throw new MsgException("获取串口数据协议出错", e);
        }

    }

    /**
     * 根据串口协议id，获取串口协议
     * 
     * @return
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private ISerialDataProtocol getDataProtocol(String dataProtocolId)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Object obj = Class.forName(dataProtocolId).newInstance();
        ISerialDataProtocol protocol = (ISerialDataProtocol) obj;
        return protocol;
    }

    public void init() throws Exception {
        try {

            if (this.bcSerialModel == null) {
                throw new Exception("串口信息bcSerialModel为空");
            }

            connectSerial(this.bcSerialModel);
            registerMQListener(this.bcSerialModel, out);
            startReceiveData();
            log.info("串口：" + bcSerialModel.getComNum() + "连接成功");
        }
        catch (Exception e) {

            String msg = "";

            if (this.bcSerialModel != null) {
                msg = this.bcSerialModel.toString();
            }

            log.error("初始化串口数据出错,串口信息：" + msg, e);
            throw e;
        }
    }

    /**
     * 连接串口
     * 
     * @param bcSerialModel
     * @throws Exception
     */
    private void connectSerial(BcSerialModel bcSerialModel) throws Exception {
        String port = bcSerialModel.getComNum();

        CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(port);
        SerialPort sPort = (SerialPort) portId.open(port, comWaitTime);

        int baudRate = Integer.parseInt(bcSerialModel.getBaudRate());
        int dataBit = Integer.parseInt(bcSerialModel.getDataBit());
        int stopBit = Integer.parseInt(bcSerialModel.getStopBit());
        int parity = Integer.parseInt(bcSerialModel.getParity());

        sPort.setSerialPortParams(baudRate, dataBit, stopBit, parity);

        in = new BufferedInputStream(sPort.getInputStream());
        out = sPort.getOutputStream();
    }

    /**
     * 监听消息队列，消费请求数据消息
     * 
     * @param bcSerialModel
     * @param out
     */
    private void registerMQListener(BcSerialModel bcSerialModel, OutputStream out) {
        MqKit.consumeMsgFromSerialAskDataMQ(bcSerialModel, new MsgListener() {

            @Override
            public void onReceive(MqModel mqModel) {
                SerialModel data = (SerialModel) mqModel;

                try {

                    Date now = new Date();
                    long timeDiff = now.getTime() - lastAskDate.getTime();

                    lastAskDate = now;

                    StringBuffer strBuf = new StringBuffer();

                    for (byte b : data.getData()) {
                        strBuf.append(b + " ");
                    }

                    /**
                     * 前后两次请求不能离得太近，否则会没有数据返回,这也就要求，串口请求数据的间隔不能太短，至少要在5S以上
                     */
                    if (timeDiff < askInterval) {
                        Thread.sleep(millTrans);
                    }

                    byte[] askData = data.getData();
                    out.write(askData);
                    out.flush();

                    log.info("询问数据成功:" + strBuf.toString());

                }
                catch (IOException | InterruptedException e) {
                    log.error("询问数据出错", e);
                }
            }
        });
    }

    private void startReceiveData() {
        Thread t1 = new Thread(this);
        t1.start();
    }

    @Override
    public void run() {

        byte[] readBuffer = new byte[readBufferLength];

        while (true) {

            try {

                int dataLength = in.read(readBuffer);

                if (dataLength <= 0) {
                    continue;
                }

                List<SerialModel> serialDataModels = dataProtocol.decode(readBuffer, dataLength,
                        bcSerialModel.getComNum());

                StringBuffer strBuf = new StringBuffer();
                 
                if(serialDataModels != null) {
                    for (SerialModel model : serialDataModels) {

                        for (byte b : model.getData()) {
                            strBuf.append(b);
                        }
                        strBuf.append("\r\n");
                    }
                }

                log.info("获取到数据：" + strBuf);

                if (serialDataModels == null || serialDataModels.size() <= 0) {
                    continue;
                }

                for (SerialModel model : serialDataModels) {
                    MqKit.productMsgToSerialMQ(model);
                }

            }
            catch (Exception e) {
                log.error("读取数据失败", e);
            }
        }

    }

}
